/*
 *  Graphics.cpp
 *  c_Render
 *
 *  Created by liao peterson on 3/16/10.
 *  Copyright 2010 uestc. All rights reserved.
 *
 */

#include "Graphics.h"
void RenderObject::render()
{
	glVertexAttrib1f(7, (GLfloat)type);
	glBindBuffer(GL_ARRAY_BUFFER, vboid);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); 
	glBindBuffer(GL_ARRAY_BUFFER, nboid);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
	
	if(TextureEnabled)
	{
		glBindBuffer(GL_ARRAY_BUFFER, tcoboid);
		glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, 0, 0);
		glBindTexture(GL_TEXTURE_2D, textureid);
	}
}

void RenderObject::InitTexture(GLuint texid)
{
	textureid = texid;
}

/**************************************************************************************
 **********BELOW CODING FOR RenderModel**********************************************
 **************************************************************************************
 */
RenderModel::RenderModel()
{
	type = MODEL;
	MeshRef = new Mesh;
	PhysicRef = NULL;
	PhysicsEnabled = 0;
}

RenderModel::RenderModel(Mesh*mTarget,physicsIntegrator*pTarget)
{
	MeshRef = mTarget;
	PhysicRef = pTarget;
	type = MODEL;
	PhysicsEnabled = 1;
	
	glGenBuffers(1, &vboid);
	glBindBuffer(GL_ARRAY_BUFFER, vboid);
	glBufferData(GL_ARRAY_BUFFER, mTarget->indexCount*3*sizeof(GLfloat)
				 , mTarget->currentVertex, GL_STATIC_DRAW);
	
	glGenBuffers(1, &nboid);
	glBindBuffer(GL_ARRAY_BUFFER, nboid);
	glBufferData(GL_ARRAY_BUFFER, mTarget->indexCount*3*sizeof(GLfloat)
				 , mTarget->currentNormal, GL_STATIC_DRAW);
	
	glGenBuffers(1, &tcoboid);
	glBindBuffer(GL_ARRAY_BUFFER, tcoboid);
	glBufferData(GL_ARRAY_BUFFER, mTarget->indexCount*2*sizeof(GLfloat)
				 , mTarget->uv, GL_STATIC_DRAW);
	
	if((TextureEnabled=mTarget->texEnable)==1)
		InitTexture();
}

void RenderModel::InitTexture()
{
	InitTexture(MeshRef->filename);
}

void RenderModel::InitTexture(GLchar*filename)
{
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glGenTextures(1, &textureid);
	
	//printf("\nfilename:%s\n",iterate->data->filename);
	MeshRef->LoadTexture(filename);
	glBindTexture(GL_TEXTURE_2D, textureid);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 
				 MeshRef->texWidth, MeshRef->texHeight,
				 0, GL_RGBA, GL_UNSIGNED_BYTE, MeshRef->textureData);
	glGenerateMipmap(GL_TEXTURE_2D);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
	//glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}

void RenderModel::render(Matrix4x4*World2Camera,GLint m2w_Handle,GLint m_u_mvpHandle)
{
	glVertexAttrib1f(7, (GLfloat)type);
	glBindBuffer(GL_ARRAY_BUFFER, vboid);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); 
	glBindBuffer(GL_ARRAY_BUFFER, nboid);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
	
	if(TextureEnabled)
	{
		glBindBuffer(GL_ARRAY_BUFFER, tcoboid);
		glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, 0, 0);
		glBindTexture(GL_TEXTURE_2D, textureid);
	}
	
	Matrix4x4 t_matrix;
	if(PhysicsEnabled)
	{
		// Transform in physics integrator already include the model to world tranform, for getAxis function for physics integrator class
		t_matrix=(*World2Camera)*(PhysicRef->transform);	
		glUniformMatrix4fv(m2w_Handle, 1, GL_FALSE, (GLfloat*)&PhysicRef->transform.m[0]);
	}else{
		t_matrix=(*World2Camera)*(*MeshRef->Model2World);	
		glUniformMatrix4fv(m2w_Handle, 1, GL_FALSE, (GLfloat*)&MeshRef->Model2World->m[0]);
	}
	glUniformMatrix4fv(m_u_mvpHandle, 1, GL_FALSE, (GLfloat*)&t_matrix.m[0]);
	
	glVertexAttrib1f(7,(GLfloat)type);
	if(0)
	{
		glDrawArrays(GL_LINE_LOOP, 0, MeshRef->triangleCount*3);
	}else{
		glDrawArrays(GL_TRIANGLES, 0, MeshRef->triangleCount*3);
	}
}

/**************************************************************************************
 **********BELOW CODING FOR RenderTexture**********************************************
 **********AND I'M THE LOVELY SEPERATOR AGAIN:)****************************************
 **************************************************************************************
 */

/*RenderTexture::RenderTexture()
{
	type = TEXTURE;
}*/

// Bottom Left for (-1.0,-1.0) Upper right for (1.0,1.0);
// starting and ending coordinate in clip space(-1.0-1.0);
RenderTexture::RenderTexture(GLfloat NstartX,GLfloat NstartY,GLfloat NendX,GLfloat NendY)
{	
	type = TEXTURE;
	TextureEnabled = 0;
	
	startX = NstartX;
	startY = NstartY;
	endX = NendX;
	endY = NendY;
	
	GLfloat vertexCo[] = {startX,startY,-1.0f,
		endX,startY,-1.0f,
		endX,endY,-1.0f,
		startX,startY,-1.0f,
		endX,endY,-1.0f,
	startX,endY,-1.0f};
	
	GLfloat vertexNo[] = {0.0f,0.0f,1.0f,
		1.0f,0.0f,1.0f,
		1.0f,1.0f,1.0f,
		0.0f,0.0f,1.0f,
		1.0f,1.0f,1.0f,
	0.0f,1.0f,1.0f};
	
	GLfloat uv[] = {0.0f,0.0f,
		1.0f,0.0f,
		1.0f,1.0f,
		0.0f,0.0f,
		1.0f,1.0f,
	0.0f,1.0f};
	
	glGenBuffers(1, &vboid);
	glBindBuffer(GL_ARRAY_BUFFER, vboid);
	glBufferData(GL_ARRAY_BUFFER, 18*sizeof(GLfloat)
				 , vertexCo, GL_STATIC_DRAW);
	
	glGenBuffers(1, &nboid);
	glBindBuffer(GL_ARRAY_BUFFER, nboid);
	glBufferData(GL_ARRAY_BUFFER, 18*sizeof(GLfloat)
				 , vertexNo, GL_STATIC_DRAW);
	
	glGenBuffers(1, &tcoboid);
	glBindBuffer(GL_ARRAY_BUFFER, tcoboid);
	glBufferData(GL_ARRAY_BUFFER, 12*sizeof(GLfloat)
				 , uv, GL_STATIC_DRAW);
}

RenderTexture::RenderTexture(GLchar*filename,GLfloat NstartX,GLfloat NstartY,GLfloat NendX,GLfloat NendY,
												GLfloat NcoXs,GLfloat NcoYs,GLfloat NcoXe,GLfloat NcoYe)
{		
	TextureEnabled = 1;
	InitTexture(filename);
	
	//RenderTexture(NstartX,NstartY,NendX,NendY);
	type = TEXTURE;
	
	startX = NstartX;
	startY = NstartY;
	endX = NendX;
	endY = NendY;
	
	coXs = NcoXs;
	coYs = NcoYs;
	coXe = NcoXe;
	coYe = NcoYe;
	
	GLfloat vertexCo[] = {startX,startY,-1.0f,
		endX,startY,-1.0f,
		startX,endY,-1.0f,
		endX,endY,-1.0f};
	
	GLfloat vertexNo[] = {0.0f,0.0f,1.0f,
		1.0f,0.0f,1.0f,
		1.0f,1.0f,1.0f,
		0.0f,0.0f,1.0f};
	
	GLfloat uv[] = {coXs,coYs,
		coXe,coYs,
		coXs,coYe,
	coXe,coYe};
	
	glGenBuffers(1, &vboid);
	glBindBuffer(GL_ARRAY_BUFFER, vboid);
	glBufferData(GL_ARRAY_BUFFER, 12*sizeof(GLfloat)
				 , vertexCo, GL_STATIC_DRAW);
	
	glGenBuffers(1, &nboid);
	glBindBuffer(GL_ARRAY_BUFFER, nboid);
	glBufferData(GL_ARRAY_BUFFER, 12*sizeof(GLfloat)
				 , vertexNo, GL_STATIC_DRAW);
	
	glGenBuffers(1, &tcoboid);
	glBindBuffer(GL_ARRAY_BUFFER, tcoboid);
	glBufferData(GL_ARRAY_BUFFER, 8*sizeof(GLfloat)
				 , uv, GL_STATIC_DRAW);
}

void RenderTexture::InitTexture(GLchar*filename)
{
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glGenTextures(1, &textureid);
	
	//printf("\nfilename:%s\n",iterate->data->filename);
	CFStringRef path = CFStringCreateWithCString(NULL,filename,kCFStringEncodingUTF8);
	CFBundleRef	bundle = CFBundleGetMainBundle();
	CFURLRef fileURL = CFBundleCopyResourceURL(bundle, path,NULL, NULL);
	CGDataProviderRef provider = CGDataProviderCreateWithURL(fileURL);
	CGImageRef textureImage = CGImageCreateWithJPEGDataProvider(provider,
																NULL, 
																true, 
																kCGRenderingIntentDefault);
	if(textureImage == NULL)
	{
		textureImage = CGImageCreateWithPNGDataProvider(provider,
														NULL, 
														true, 
														kCGRenderingIntentDefault);
	}
	
	CFRelease(path);
	CFRelease(fileURL);
	CGDataProviderRelease(provider);
	//CFRelease(bundle);
	if(textureImage==nil){
		perror("ERROR");
		printf("Failed to load texture image");
		return;
	}
	texWidth=CGImageGetWidth(textureImage);
	texHeight=CGImageGetHeight(textureImage);
	
	textureData=(GLubyte*)malloc(texWidth*texHeight*4);
	
	CGContextRef textureContext=CGBitmapContextCreate(textureData,
													  texWidth, 
													  texHeight, 
													  8, 
													  texWidth*4, 
													  CGImageGetColorSpace(textureImage),
													  kCGImageAlphaPremultipliedLast);
	assert(textureContext);
	CGContextDrawImage(textureContext,
					   CGRectMake(0.0, 0.0, (GLfloat)texWidth, (GLfloat)texHeight), 
					   textureImage);
	
	CGImageRelease(textureImage);
	CGContextRelease(textureContext);
	
	glBindTexture(GL_TEXTURE_2D, textureid);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 
				 texWidth, texHeight,
				 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	
	//initialize(textureid, -1.0f,0.5f,1.0f,0.9f);
}

void RenderTexture::render()
{
	glVertexAttrib1f(7, (GLfloat)type);
	glBindBuffer(GL_ARRAY_BUFFER, vboid);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); 
	glBindBuffer(GL_ARRAY_BUFFER, nboid);
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
	
	if(TextureEnabled)
	{
		glBindBuffer(GL_ARRAY_BUFFER, tcoboid);
		glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, 0, 0);
		glBindTexture(GL_TEXTURE_2D, textureid);
	}
	
	if(0)
	{
		glDrawArrays(GL_LINE_LOOP, 0, 4);
	}else{
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	}
}

void RenderTexture::render(GLfloat NstartX,GLfloat NstartY,GLfloat NendX,GLfloat NendY,
			GLfloat NcoXs,GLfloat NcoYs,GLfloat NcoXe,GLfloat NcoYe)
{
	// Don't know why this doesn't work
	/*const GLfloat vertexCo[] = {NstartX,NstartY,-1.0f,
		NendX,NstartY,-1.0f,
		NstartX,NendY,-1.0f,
	NendX,NendY,-1.0f};
	
	const GLfloat vertexNo[] = {0.0f,0.0f,1.0f,
		1.0f,0.0f,1.0f,
		1.0f,1.0f,1.0f,
	0.0f,0.0f,1.0f};
	
	const GLfloat uv[] = {NcoXs,NcoYs,
		NcoXe,NcoYs,
		NcoXs,NcoYe,
	NcoXe,NcoYe};
	
	glVertexAttrib1f(7, (GLfloat)type);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, &vertexCo[0]); 
	glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, &vertexNo[0]);
	
	if(TextureEnabled)
	{
		glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, 0, &uv[0]);
		glBindTexture(GL_TEXTURE_2D, textureid);
	}
	
	if(0)
	{
		glDrawArrays(GL_LINE_LOOP, 0, 4);
	}else{
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	}*/
	
	const GLfloat vertexCo[] = {NstartX,NstartY,-1.0f,
		NendX,NstartY,-1.0f,
		NstartX,NendY,-1.0f,
	NendX,NendY,-1.0f};
	
	const GLfloat vertexNo[] = {0.0f,0.0f,1.0f,
		1.0f,0.0f,1.0f,
		1.0f,1.0f,1.0f,
	0.0f,0.0f,1.0f};
	
	const GLfloat uv[] = {NcoXs,NcoYs,
		NcoXe,NcoYs,
		NcoXs,NcoYe,
	NcoXe,NcoYe};
	
	startX = NstartX;
	startY = NstartY;
	endX = NendX;
	endY = NendY;
	
	coXs = NcoXs;
	coYs = NcoYs;
	coXe = NcoXe;
	coYe = NcoYe;
	
	glGenBuffers(1, &vboid);
	glBindBuffer(GL_ARRAY_BUFFER, vboid);
	glBufferData(GL_ARRAY_BUFFER, 12*sizeof(GLfloat)
				 , vertexCo, GL_STATIC_DRAW);
	
	glGenBuffers(1, &nboid);
	glBindBuffer(GL_ARRAY_BUFFER, nboid);
	glBufferData(GL_ARRAY_BUFFER, 12*sizeof(GLfloat)
				 , vertexNo, GL_STATIC_DRAW);
	
	glGenBuffers(1, &tcoboid);
	glBindBuffer(GL_ARRAY_BUFFER, tcoboid);
	glBufferData(GL_ARRAY_BUFFER, 8*sizeof(GLfloat)
				 , uv, GL_STATIC_DRAW);
	render();
}

void RenderTexture::render(GLfloat NstartX,GLfloat NstartY,Font ch,GLfloat scale)
{
	GLfloat ratio = texWidth/texHeight;
	render(NstartX,NstartY,NstartX + (ch.data[2] - ch.data[0])*ratio*scale,
							NstartY + (ch.data[3] - ch.data[1])*scale,
							ch.data[0],ch.data[1],ch.data[2],ch.data[3]);
}

void RenderTexture::render(GLfloat NstartX,GLfloat NstartY,GLfloat NendX,GLfloat NendY,Font ch)
{
	render(NstartX,NstartY,NendX,NendY,
		   ch.data[0],ch.data[1],ch.data[2],ch.data[3]);
}